<?php
/**
 * SimplifyPHP Framework
 *
 * This file is part of SimplifyPHP Framework.
 *
 * SimplifyPHP Framework is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SimplifyPHP Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Rodrigo Rutkoski Rodrigues <rutkoski@gmail.com>
 * @copyright Copyright 2008 Rodrigo Rutkoski Rodrigues
 */

require_once(dirname(__file__).'/Dictionary.php');
require_once(dirname(__file__).'/IDataHolder.php');
require_once(dirname(__file__).'/filter/IFilter.php');
require_once(dirname(__file__).'/validation/IValidator.php');

/**
 * Data Holder provides extra functionality to Dictionary, such as getters, setters,
 * validators, validator methods, filters and filter methods.
 *
 * Getters:
 *   protected function get_<name>($default = null, $filter = null) {
 *     return $this->_get('<name>', $default, $filter);
 *   }
 *
 * Setters:
 *   protected function set_<name>($value = null) {
 *     $this->_set('<name>', $value);
 *   }
 *
 * Validator Methods:
 *   protected function validate_<name>() {
 *     throw new ValidationException('Validation error message');
 *   }
 *
 * Filter Methods:
 *   protected function filter_<name>($value) {
 *     return $value;
 *   }
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 * @package Simplify_Kernel_Data
 */
class DataHolder extends Dictionary implements IDataHolder
{

  /**
   * Validation errors.
   *
   * @var array
   */
  protected $errors = array();

  /**
   * Filters.
   *
   * @var array
   */
  protected $filters = array();

  /**
   * Validators.
   *
   * @var array
   */
  protected $rules = array();

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/data/IDataHolder#getErrors()
   */
  public function getErrors()
  {
    return $this->errors;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/data/Dictionary#get($name, $default, $filter)
   */
  public function get($name, $default = null, $filter = null)
  {
    $getter = 'get_' . $name;
    if (method_exists($this, $getter)) {
      return $this->$getter($default, $filter);
    }

    return parent::get($name, $default, $filter);
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/data/Dictionary#set($name, $value)
   */
  public function set($name, $value = null)
  {
    $setter = 'set_' . $name;
    if (method_exists($this, $setter)) {
      $this->$setter($value);
    }
    else {
      parent::set($name, $value);
    }
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/data/IDataHolder#setFilter($name, $filter)
   */
  public function setFilter($name, IFilter $filter = null)
  {
    $this->filters[$name] = $filter;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/data/IDataHolder#setRule($name, $rule)
   */
  public function setRule($name, IValidator $rule = null)
  {
    $this->rules[$name] = $rule;
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/data/IDataHolder#validate($name)
   */
  public function validate($name)
  {
    $func = 'validate_'.$name;
    if (method_exists($this, $func)) {
      call_user_func(array($this, $func));
    }

    else if (isset($this->rules[$name])) {
      $this->rules[$name]->validate($this->get($name));
    }
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/data/IDataHolder#validateAll()
   */
  public function validateAll()
  {
    $this->errors = array();

    foreach ($this->rules as $name=>$rule) {
      try {
        $rule->validate($this->get($name));
      } catch (ValidationException $e) {
        $this->errors[$name] = $rule->getError();
      }
    }

    // call validator methods
    $methods = get_class_methods($this);
    foreach ($methods as $method) {
      if (strpos($method, 'validate_') === 0) {
        try {
          call_user_func(array($this, $method));
        } catch (ValidationException $e) {
          $name = substr($method, 9);
          $this->errors[$name] = $e->getMessage();
        }
      }
    }

    if (! empty($this->errors)) throw new ValidationException('Validation failed');
  }

  /**
   * Parse $data as a Data Holder.
   *
   * @param mixed $data $data for the Data Holder.
   * @return DataHolder
   */
  public static function parseFrom($data)
  {
    if (is_a($data, 'DataHolder')) return $data;
    if (method_exists($data, 'getAll')) $data = $data->getAll();
    return new DataHolder($data);
  }

  /**
   * Apply a filter.
   *
   * @param string $name The key.
   * @return mixed Filtered value.
   */
  protected function filter($name)
  {
    $value = $this->get($name);

    if (isset($this->filters['*']))
    $value = $this->filters['*']->filter($value);

    $func = 'filter_'.$name;
    if (method_exists($this, $func)) {
      $value = call_user_func(array($this, $func), $value);
    }

    if (isset($this->filters[$name]))
    $value = $this->filters[$name]->filter($value);

    return $value;
  }

  /**
   * Internal getter. Ignores filters and getters.
   * @see simplify/kernel/data/Dictionary#get($name, $default, $defaultIfEmpty)
   */
  protected function _get($name, $default = null, $defaultIfEmpty = false)
  {
    return parent::get($name, $default, $defaultIfEmpty);
  }

  /**
   * Internal setter. Ignores filters and setters.
   * @see simplify/kernel/data/Dictionary#set($name, $value)
   */
  protected function _set($name, $value = null)
  {
    parent::set($name, $value);
  }

}

?>